import numpy as np
from math import sqrt
from collections import Counter
from metrics import accuracy_score

class KNN_classifier:
    
    def __init__(self,k):
        # 初始化KNN分类器
        assert k>=1
        self.k=k
        self._X_train=None#私有变量【只有类内可以调用】，下面的方法不return _X_train的话就调用不了
        self._y_train=None

    def fit(self,X_train,y_train):
        assert self.k<=X_train.shape[0]#断言语句，确保k在1与x_train行数之间
        assert X_train.shape[0]==y_train.shape[0]#每个向量都要有对应的标签
        
        
        
        # 根据训练数据集X_train,y_train训练knn分类器
        self._X_train=X_train
        self._y_train=y_train
        return self#【重要！！】实现方法链式调用，使得带着传入对象的方法可以继续执行其他方法（带着传入的东西）
    
    def predict(self,X_predict):
    # 传入待预测数据集X_predict
        """此时_X_train和_y_train不应为空了"""
        assert self._X_train is not None and self._y_train is not None
        """保证预测的向量特征数量与训练集特征数量一样一样"""
        assert X_predict.shape[1]==self._X_train.shape[1]
       
        y_predict=[self._predict(x) for x in X_predict ]
        return np.array(y_predict)
    
    def _predict(self,x):
        """给定单个待预测的数据，返回x的预测值"""
        assert x.shape[0]==self._X_train.shape[1]#【x.shape返回的是元组（n,）形式,n代表了n列也就是特征个数】确保训练集的特征个数与输入的特征个数一样
        
        
        distances=[sqrt(np.sum((x_train - x) ** 2)) for x_train in self._X_train]
        nearest=np.argsort(distances)
        topK_y=[self._y_train[i] for i in nearest[:self.k]]
        votes=Counter(topK_y)
        return votes.most_common(1)[0][0]
    def score(self,X_test,Y_test):#预测+匹配度=>直接出匹配度
        y_predict=self.predict(X_test)
        return accuracy_score(Y_test,y_predict)
    def __reper__(self):
        return "KNN(k=%d)"%self.k




